package com.young.coderule.common.aspect;

import com.young.coderule.common.annotation.CodeRule;
import com.young.coderule.common.lock.MyLock;
import com.young.coderule.common.scan.ResourceDefinition;
import com.young.coderule.common.util.MyUtils;
import com.young.coderule.entity.ComCodeRule;
import com.young.coderule.entity.ComCodeRuleNumber;
import com.young.coderule.service.CodeRuleCacheService;
import com.young.coderule.service.ComCodeRuleNumberService;
import com.young.coderule.service.ComCodeRuleService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

/**
 * @DESCRIPTION:
 * @USER: Young
 */
@Aspect
@Component
public class CodeRulesAspect {
    @Autowired
    private CodeRuleCacheService codeRuleCacheService;

    @Autowired
    private ComCodeRuleNumberService comCodeRuleNumberService;

    @Autowired
    private ComCodeRuleService codeRuleService;

    @Autowired
    private MyLock mylock;

    /**
     * 定义一个切面，拦截CodeRule注解标注方法
     */
    @Pointcut("@annotation(com.young.coderule.common.annotation.CodeRule)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = null;
        Object[] args = joinPoint.getArgs();

        String bizObjectCode = getBizObjectCode(joinPoint);
        try {
            ComCodeRule comCodeRule = codeRuleService.queryDetail(bizObjectCode);
            if(null != comCodeRule){
                try{
                    Boolean acquiredLock = mylock.lock(bizObjectCode);
                    //未获取到锁，重试几次
                    for(int i = 0; !acquiredLock && i < 3; ++i) {
                        acquiredLock = this.mylock.lock(bizObjectCode);
                    }
                    if(!acquiredLock){
                        throw new Exception("获取编码异常，为获取到编码规则锁");
                    }

                    ComCodeRuleNumber comCodeRuleNumber = codeRuleCacheService.getCodeRuleNumber(bizObjectCode);
                    if(null!=comCodeRuleNumber) {
                        // 并发获取了同一个编码，需要在此获取最新的。根据该实体的编码标识重新赋值
                        //如果不可以修改才重新赋值
                        if(!"Y".equals(comCodeRule.getIsmodifiable())){
                            BeanMap beanMap = BeanMap.create(args[0]);
                            beanMap.put(comCodeRule.getCodeField(), codeRuleService.getCode(bizObjectCode));
                        }

                        //业务方法执行
                        result = joinPoint.proceed(args);

                        //可以判断业务执行结果，再更新最大号。但是方法执行结果不能保证一致，不方便取结果

                        //业务方法执行后
                        //先缓存，再数据库。缓存可以修改
                        ComCodeRuleNumber comCodeRuleNumber_new = codeRuleCacheService.getCodeRuleNumber(bizObjectCode);
                        Integer bigNumber = comCodeRuleNumber_new.getBigNumber();
                        bigNumber++;
                        comCodeRuleNumber_new.setBigNumber(bigNumber);

                        // 更新缓存并保存数据库
                        if (null != comCodeRuleNumber_new.getId() && comCodeRuleNumber_new.getId() != 0) {
                            comCodeRuleNumberService.edit(comCodeRuleNumber_new);
                        } else {
                            comCodeRuleNumberService.add(comCodeRuleNumber_new);
                        }
                        return result;
                    }

                }finally {
                    mylock.unlock(bizObjectCode);
                }

            }

            return joinPoint.proceed(args);
        } catch (Throwable e) {
            throw e;
        }
    }
/*
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        BeanMap beanMap = BeanMap.create(args[0]);

        String bizObjectCode = getBizObjectCode(joinPoint);
        if(StringUtils.isEmpty(bizObjectCode)) return ;

        ComCodeRule comCodeRule = codeRuleService.queryDetail(bizObjectCode);

        if(null==comCodeRule) return;

        Boolean acquiredLock = mylock.lock(bizObjectCode);
        //未获取到锁，重试几次
        for(int i = 0; !acquiredLock && i < 3; ++i) {
            acquiredLock = this.mylock.lock(bizObjectCode);
        }
        if(!acquiredLock){
            // TODO 没获取到锁，处理
            return;
        }

        ComCodeRuleNumber comCodeRuleNumber = codeRuleCacheService.getCodeRuleNumber(bizObjectCode);
        if(null==comCodeRuleNumber) {
            mylock.unlock(bizObjectCode);
            return ;
        }

        // 并发获取了同一个编码，需要在此获取最新的。根据该实体的编码标识重新赋值
        //如果不可以修改才重新赋值
        if(!"Y".equals(comCodeRule.getIsmodifiable())){

            beanMap.put(comCodeRule.getCodeField(), codeRuleService.getCode(bizObjectCode));
        }

    }

    @AfterReturning(returning = "obj",pointcut ="pointCut()")
    public void doAfterReturning(JoinPoint joinPoint,Object obj) {
        //保存的时候存最大号表
        //缓存中没取到就直接过
        //取到了就判断和数据库里的大小，存大的那个
        String bizObjectCode = getBizObjectCode(joinPoint);
        if(StringUtils.isEmpty(bizObjectCode)) return ;
        Boolean isOwner = mylock.isOwner(bizObjectCode);
        if(isOwner){
            try{
                //先缓存，再数据库。缓存可以修改
                ComCodeRuleNumber comCodeRuleNumber = codeRuleCacheService.getCodeRuleNumber(bizObjectCode);
                Integer bigNumber = comCodeRuleNumber.getBigNumber();
                bigNumber++;
                comCodeRuleNumber.setBigNumber(bigNumber);

                // 更新缓存并保存数据库
                if (null != comCodeRuleNumber.getId() && comCodeRuleNumber.getId() != 0) {
                    comCodeRuleNumberService.edit(comCodeRuleNumber);
                } else {
                    comCodeRuleNumberService.add(comCodeRuleNumber);
                }

            }catch (Exception e){

            }finally {
                mylock.unlock(bizObjectCode);
            }
        }

    }*/

    @AfterThrowing(throwing="ex",pointcut ="pointCut()")
    public void doAfterThrowing(JoinPoint joinPoint,Exception ex)  {
        //保存的时候存最大号表
        //可能需要修改编号
        String bizObjectCode = getBizObjectCode(joinPoint);
        if(StringUtils.isEmpty(bizObjectCode)) return ;
        //异常处理
        mylock.unlock(bizObjectCode);
    }


    private String getBizObjectCode(JoinPoint joinPoint) {
        // 获取实体标识
        ResourceDefinition resourceDefinition = MyUtils.getResourceDefinition(((MethodSignature)joinPoint.getSignature()).getMethod().getAnnotation(CodeRule.class), joinPoint.getSignature().getDeclaringType().getSimpleName());

        return resourceDefinition.getCode();
    }
}
